/*
 * @Author: humengchuan 531537052@qq.com
 * @Date: 2025-10-24 15:38:56
 * @LastEditors: humengchuan 531537052@qq.com
 * @LastEditTime: 2025-10-24 21:12:18
 * @FilePath: \work-tool\experi-api\utils\redisStroe.js
 * @Description: Redis缓存操作工具类
 */
const redis = require('redis');

// Redis客户端实例
let CLIENT = null;
// 连接状态
let isConnected = false;

/**
 * 初始化Redis客户端连接
 * @param {Object} options - Redis连接配置
 * @returns {Promise<Object>} Redis客户端实例
 */
const initRedisClient = async(options = {}) => {
  try {
    // 如果客户端已存在且连接正常，直接返回
    if (CLIENT && isConnected) {
      return CLIENT;
    }

    // 默认配置
    const defaultOptions = {
      url: process.env.REDIS_URL || 'redis://localhost:6379',
      socket: {
        connectTimeout: 5000,
        keepAlive: 10000,
        reconnectStrategy: (retries) => Math.min(retries * 50, 2000)
      }
    };

    // 合并配置
    const config = { ...defaultOptions, ...options };

    // 创建Redis客户端
    CLIENT = redis.createClient(config);

    // 错误处理
    CLIENT.on('error', (err) => {
      console.error('Redis客户端错误:', err);
      isConnected = false;
    });

    // 连接成功
    CLIENT.on('connect', () => {
      console.log('Redis连接中...');
    });

    // 准备就绪
    CLIENT.on('ready', () => {
      console.log('Redis连接成功');
      isConnected = true;
    });

    // 连接关闭
    CLIENT.on('end', () => {
      console.log('Redis连接关闭');
      isConnected = false;
    });

    // 重连
    CLIENT.on('reconnecting', (params) => {
      console.log(`Redis重连中，尝试次数: ${params.attempt}`);
    });

    // 连接Redis
    await CLIENT.connect();
    return CLIENT;
  } catch (error) {
    console.error('Redis初始化失败:', error);
    throw new Error(`Redis连接失败: ${error.message}`);
  }
};

/**
 * 确保Redis客户端已连接
 * @returns {Promise<Object>} Redis客户端实例
 */
const ensureClient = async() => {
  if (!CLIENT || !isConnected) {
    return await initRedisClient();
  }
  return CLIENT;
};

/**
 * 设置缓存
 * @param {string} key - 缓存键
 * @param {any} value - 缓存值
 * @param {number} expire - 过期时间(秒)，默认不过期
 * @returns {Promise<boolean>} 是否设置成功
 */
const redisSet = async(key, value, expire = null) => {
  try {
    const client = await ensureClient();
    
    // 序列化值
    const val = JSON.stringify(value);
    
    // 设置缓存
    await client.set(key, val);
    
    // 设置过期时间
    if (expire && typeof expire === 'number' && expire > 0) {
      await client.expire(key, expire);
    }
    
    return true;
  } catch (error) {
    console.error(`Redis设置缓存失败 [${key}]:`, error);
    return false;
  }
};

/**
 * 获取缓存
 * @param {string} key - 缓存键
 * @returns {Promise<any>} 缓存值，失败返回null
 */
const redisGet = async(key) => {
  try {
    const client = await ensureClient();
    
    const val = await client.get(key);
    
    // 如果值不存在，返回null
    if (val === null) {
      return null;
    }
    
    // 反序列化值
    return JSON.parse(val);
  } catch (error) {
    console.error(`Redis获取缓存失败 [${key}]:`, error);
    return null;
  }
};

/**
 * 删除缓存
 * @param {string} key - 缓存键
 * @returns {Promise<boolean>} 是否删除成功
 */
const redisDel = async(key) => {
  try {
    const client = await ensureClient();
    await client.del(key);
    return true;
  } catch (error) {
    console.error(`Redis删除缓存失败 [${key}]:`, error);
    return false;
  }
};

/**
 * 批量设置缓存
 * @param {Array} keyValuePairs - 键值对数组，格式: [[key1, value1], [key2, value2]]
 * @param {number} expire - 过期时间(秒)，默认不过期
 * @returns {Promise<boolean>} 是否设置成功
 */
const redisMSet = async(keyValuePairs, expire = null) => {
  try {
    const client = await ensureClient();
    
    // 检查输入格式
    if (!Array.isArray(keyValuePairs)) {
      throw new Error('keyValuePairs必须是数组格式');
    }
    
    // 处理键值对，序列化值
    const pairs = [];
    for (const [key, value] of keyValuePairs) {
      pairs.push(key);
      pairs.push(JSON.stringify(value));
    }
    
    // 批量设置
    await client.mSet(pairs);
    
    // 如果设置了过期时间，为每个键设置过期
    if (expire && typeof expire === 'number' && expire > 0) {
      for (const [key] of keyValuePairs) {
        await client.expire(key, expire);
      }
    }
    
    return true;
  } catch (error) {
    console.error('Redis批量设置缓存失败:', error);
    return false;
  }
};

/**
 * 批量获取缓存
 * @param {Array<string>} keys - 缓存键数组
 * @returns {Promise<Array>} 缓存值数组
 */
const redisMGet = async(keys) => {
  try {
    const client = await ensureClient();
    
    // 检查输入格式
    if (!Array.isArray(keys)) {
      throw new Error('keys必须是数组格式');
    }
    
    const values = await client.mGet(keys);
    
    // 反序列化值
    return values.map(val => {
      try {
        return val ? JSON.parse(val) : null;
      } catch {
        return null;
      }
    });
  } catch (error) {
    console.error('Redis批量获取缓存失败:', error);
    return null;
  }
};

/**
 * 检查键是否存在
 * @param {string} key - 缓存键
 * @returns {Promise<boolean>} 是否存在
 */
const redisExists = async(key) => {
  try {
    const client = await ensureClient();
    const result = await client.exists(key);
    return result > 0;
  } catch (error) {
    console.error(`Redis检查键是否存在失败 [${key}]:`, error);
    return false;
  }
};

/**
 * 设置过期时间
 * @param {string} key - 缓存键
 * @param {number} seconds - 过期时间(秒)
 * @returns {Promise<boolean>} 是否设置成功
 */
const redisExpire = async(key, seconds) => {
  try {
    const client = await ensureClient();
    await client.expire(key, seconds);
    return true;
  } catch (error) {
    console.error(`Redis设置过期时间失败 [${key}]:`, error);
    return false;
  }
};

/**
 * 获取剩余过期时间
 * @param {string} key - 缓存键
 * @returns {Promise<number>} 剩余过期时间(秒)，-1表示永不过期，-2表示键不存在
 */
const redisTTL = async(key) => {
  try {
    const client = await ensureClient();
    return await client.ttl(key);
  } catch (error) {
    console.error(`Redis获取过期时间失败 [${key}]:`, error);
    return -2;
  }
};

/**
 * 关闭Redis连接
 * @returns {Promise<void>}
 */
const closeRedisClient = async() => {
  try {
    if (CLIENT) {
      await CLIENT.quit();
      CLIENT = null;
      isConnected = false;
      console.log('Redis连接已关闭');
    }
  } catch (error) {
    console.error('关闭Redis连接失败:', error);
  }
};


module.exports = {
  // 基础操作
  redisSet,
  redisGet,
  redisDel,
  
  // 批量操作
  redisMSet,
  redisMGet,
  
  // 状态检查与控制
  redisExists,
  redisExpire,
  redisTTL,
  
  // 连接管理
  initRedisClient,
  closeRedisClient,
  ensureClient
}